// GET / HTTP/1.1
// Host: 110.41.14.69:8088
// Connection: keep-alive
// Cache-Control: max-age=0
// Upgrade-Insecure-Requests: 1
// User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0
// Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
// Accept-Encoding: gzip, deflate
// Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6


#include "/home/hrj/include/Log.hpp"
#include <iostream>
#include <unordered_map>
#include <vector>
#include <sstream>
#include <filesystem>
#include "/home/hrj/include/Log.hpp"

const std::string Seq = "\r\n";
const std::string LineSeq = " "; 
const std::string HeaderSeq = ": ";
const std::string Path = "/home/hrj/myweb/httpserver/wwwroot"; // 前端文件默认的路径
const std::string ErrorPage = Path + "/404page.html";
const std::string HttpVersion = "HTTP/1.0";

namespace HttpProtocalModule
{
    using namespace LogModule;
    class Request
    {
    private:
        bool ParseOneLine(std::string& req,std::string* line,const std::string& seq = Seq)
        {
            size_t pos = req.find(seq);
            if(pos == std::string::npos) return false;
            *line = req.substr(0,pos);
            req.erase(0,pos + seq.size());
            return true;
        }
        bool ParseFirstLine(std::string& req)
        {
            // 通过\r\n获取到第一行
            
            std::string first_line;
            if(ParseOneLine(req,&first_line,Seq) == false) return false;
            std::stringstream ssm(first_line);
            // 获取请求的信息
            ssm >> _method >> _uri >> _version;
            if(_uri == "/") _uri = "/index.html";
            return true;
        }

        bool ParseHeader(std::string& req)
        {
            // 获取请求报头
            std::string line;
            while(true)
            {
                ParseOneLine(req,&line,Seq);
                // key : value
                if(line.empty()) break;
                size_t pos = line.find(HeaderSeq);
                if(pos == std::string::npos) return false;
                _header.emplace(line.substr(0,pos),line.substr(pos + HeaderSeq.size()));
            }
            return true;
        }

        bool ParseBody(std::string& req)
        {
            _body = std::move(req);
            return true;
        }
    public:   
        
        const std::string& Method() { return _method; }
        std::string Uri() { return Path + _uri; }
        const std::string& Version() { return _version; }
        const std::unordered_map<std::string,std::string> Header() { return _header; }
        const std::string& Body() { return _body; }

        bool Deserialize(std::string& req)
        {
            // 反序列化，读取http中的请求
            if(req.size() == 0) {
                LOG(LogLevel::ERROR) << "request is empty";
                return false;
            } 
            if(ParseFirstLine(req) == false) return false;
            if(ParseHeader(req) == false) return false;
            if(ParseBody(req) == false) return false;
            return true;
        }
    private:
        // 方法 + uri + 版本
        std::string _method;
        std::string _uri;
        std::string _version;

        // 请求报头
        std::unordered_map<std::string,std::string> _header;

        std::string _body;
    };

    // 建立状态码和状态描述的映射
    std::unordered_map<int,std::string> Status_Description_Map
    {
        { 200 , "OK"},
        { 404 , "Not Found" }
    };

    class Response
    {
        // 
        void AddFirstLine(std::string* req)
        {
            std::string buffer = HttpVersion + LineSeq + std::to_string(_status_code) + LineSeq + Status_Description_Map[_status_code] + Seq;
            *req += buffer;   
        }

        void AddHeader(std::string* req)
        {
            // 添加报头
            // 首先假如request中的字段
            for(auto& pair : _header)
                *req = *req + pair.first + HeaderSeq + pair.second + Seq;
        }
        // 添加空行
        void AddBlankLine(std::string* req)
        {
            *req += Seq;
        }
        void AddContent(std::string* req)
        {
            std::string body(_content.begin(),_content.end());
            *req += body;
        }
    public:

        void Serialize(std::string* req)
        {
            
            AddFirstLine(req);
            AddHeader(req);
            AddBlankLine(req);
            AddContent(req);
        }
        
        bool GetContent(const std::string& path)
        {
            // 该路径下没有对应的文件
           
            if(std::filesystem::is_directory(path))
                return false;
            if(std::filesystem::exists(path) == false) // 项目路径没有这个文件
                return false;
            // 二进制打开文件

            std::ifstream file(path,std::ios::binary);
            if(!file.is_open())
            {
                LOG(LogLevel::DEBUG) << path << " open fail!";
                return false;
            }
            LOG(LogLevel::DEBUG) << path << " is been opened";
            
            // 获取文件的大小
            file.seekg(0,std::ios::end);
            std::streampos filesize = file.tellg();
            file.seekg(0,std::ios::beg);
            _filesize = filesize;

            _content.resize(_filesize);

            // 
            file.read(reinterpret_cast<char*>(_content.data()),_filesize);
            if(!file)
            {
                LOG(LogLevel::DEBUG) << "Error reading file";
                return false;
            }
            return true;
        }
        void Build(Request& req) //通过请求构建应答
        {
            _uri = req.Uri();
            
            if(GetContent(_uri) == false)
            {
                _status_code = 404;
                GetContent(ErrorPage);
            }
            else 
                _status_code = 200;
        
            _header = req.Header();
        }
    private:
        int _status_code; // 状态码
        std::string _uri;
        std::vector<char> _content;
        std::unordered_map<std::string,std::string> _header;
        int _filesize;
    };
}